<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="qyFMa" id="qyFMa"><span data-lake-id="udd399cca" id="udd399cca">典型回答</span></h1>
  <p data-lake-id="u255ecd42" id="u255ecd42"><br></p>
  <p data-lake-id="uad5f5cad" id="uad5f5cad"><span data-lake-id="ubbbb0d5c" id="ubbbb0d5c">​</span><br></p>
  <p data-lake-id="ufa3067c4" id="ufa3067c4"><span data-lake-id="u2b79cef5" id="u2b79cef5">我们说过，没办法直接在主线程的try-catch中捕获子线程的异常。但是，有的时候子线程中会开启一些IO链接，网络资源等，那么，如何在抛出异常的时候进行处理呢？</span></p>
  <p data-lake-id="u5d50a8c5" id="u5d50a8c5"><span data-lake-id="u13c1e90b" id="u13c1e90b">​</span><br></p>
  <p data-lake-id="u27069ada" id="u27069ada"><span data-lake-id="u55cad0bc" id="u55cad0bc">有几个方案可以实现：</span></p>
  <p data-lake-id="u6fff43af" id="u6fff43af"><span data-lake-id="u59a9dad3" id="u59a9dad3">​</span><br></p>
  <h3 data-lake-id="M5ddB" id="M5ddB"><span data-lake-id="u21f0377d" id="u21f0377d">使用Future</span></h3>
  <p data-lake-id="u96ae189c" id="u96ae189c"><span data-lake-id="u65ff49d5" id="u65ff49d5">​</span><br></p>
  <p data-lake-id="ua5f82e7d" id="ua5f82e7d"><span data-lake-id="u1752b281" id="u1752b281">如果想要在主线程能够捕获子线程的异常，可以考虑使用Callable和Future，它们允许主线程获取子线程的执行结果和异常。这样，主线程可以检查子线程是否抛出了异常，并在必要时处理它。以下是一个示例：</span></p>
  <p data-lake-id="u20f1fa30" id="u20f1fa30"><span data-lake-id="ub9b8fb0c" id="ub9b8fb0c">​</span><br></p>
  <pre lang="java"><code>
import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        Future&lt;Integer&gt; future = executor.submit(() -&gt; {
            // 子线程抛出异常
            throw new RuntimeException("子线程异常");
        });

        try {
            Integer result = future.get();
            System.out.println("子线程结果: " + result);
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            System.out.println("捕获到子线程异常"));
        }

        executor.shutdown();
    }
}

</code></pre>
  <p data-lake-id="udda94f35" id="udda94f35"><br></p>
  <p data-lake-id="u9c999bb5" id="u9c999bb5"><span data-lake-id="u350f5c78" id="u350f5c78">​</span><br></p>
  <p data-lake-id="ud8ce38b5" id="ud8ce38b5"><span data-lake-id="u20a88b69" id="u20a88b69">以上代码输出结果：</span></p>
  <p data-lake-id="u78815b01" id="u78815b01"><span data-lake-id="u5f6fad95" id="u5f6fad95">​</span><br></p>
  <pre lang="java"><code>
捕获到子线程异常
</code></pre>
  <p data-lake-id="ua8bc451d" id="ua8bc451d"><br></p>
  <p data-lake-id="u64d485b8" id="u64d485b8"><span data-lake-id="u809c9287" id="u809c9287">即，子线程中抛出的异常，我们在主线程中的catch块中捕获到了。</span></p>
  <p data-lake-id="uce0970b6" id="uce0970b6"><br></p>
  <h3 data-lake-id="OjAHh" id="OjAHh"><span data-lake-id="ua0e936fb" id="ua0e936fb">UncaughtExceptionHandler</span></h3>
  <p data-lake-id="u872321fb" id="u872321fb"><br></p>
  <p data-lake-id="u890ecdba" id="u890ecdba"><span data-lake-id="u68b236d8" id="u68b236d8">我们还可以使用</span><code data-lake-id="ubddc0038" id="ubddc0038"><span data-lake-id="u1f31ee3a" id="u1f31ee3a">UncaughtExceptionHandler</span></code><span data-lake-id="u40a5295f" id="u40a5295f">，</span><code data-lake-id="u7211548f" id="u7211548f"><span data-lake-id="uec8d895f" id="uec8d895f">UncaughtExceptionHandler</span></code><span data-lake-id="u6ec93c75" id="u6ec93c75"> 是 Java 中的一个接口，用于处理未捕获异常，即那些没有被 try-catch 块捕获的异常。它允许定义自定义异常处理器，以便在线程出现未捕获异常时采取特定的操作。</span></p>
  <p data-lake-id="u38c0957b" id="u38c0957b"><span data-lake-id="u7e06935e" id="u7e06935e">​</span><br></p>
  <p data-lake-id="ubaba899b" id="ubaba899b"><span data-lake-id="ud7cebe0b" id="ud7cebe0b">有了它，我们就可以为线程设置一个自定义的未捕获异常处理器，当线程抛出未捕获异常时，该处理器会被调用，我们可以在其中记录异常信息、执行清理操作等。</span></p>
  <p data-lake-id="uf54c77f5" id="uf54c77f5"><span data-lake-id="u836ff4ff" id="u836ff4ff">​</span><br></p>
  <p data-lake-id="u8387f3a2" id="u8387f3a2"><span data-lake-id="u335a0fd1" id="u335a0fd1">以下是一个代码示例：</span></p>
  <p data-lake-id="u8e64daf4" id="u8e64daf4"><span data-lake-id="u7b6b042d" id="u7b6b042d">​</span><br></p>
  <pre lang="java"><code>
public class HollisUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("线程 " + t.getName() + " 抛出未捕获异常：" + e.getMessage());
        // 在这里可以执行自定义的异常处理逻辑
    }
}

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -&gt; {
            throw new RuntimeException("这是一个未捕获异常");
        });
        
        // 设置自定义的未捕获异常处理器
        thread.setUncaughtExceptionHandler(new HollisUncaughtExceptionHandler());
        
        thread.start();
    }
}

</code></pre>
  <p data-lake-id="ue647d835" id="ue647d835"><br></p>
  <p data-lake-id="uf1bf70bf" id="uf1bf70bf"><span data-lake-id="u12e97e38" id="u12e97e38">在这个示例中，我们创建了一个自定义的 HollisUncaughtExceptionHandler ，并将其设置为线程的未捕获异常处理器。当线程执行时抛出未捕获异常，uncaughtException 方法会被调用，然后执行我们在其中实现的处理逻辑。</span></p>
  <p data-lake-id="uee7b331e" id="uee7b331e"><br></p>
  <p data-lake-id="ue7025229" id="ue7025229"><span data-lake-id="u5ab22839" id="u5ab22839">这样也就间接实现了在主线中捕获到子线程中抛出的异常。</span></p>
  <p data-lake-id="u349f0f2b" id="u349f0f2b"><span data-lake-id="u4b02f075" id="u4b02f075">​</span><br></p>
  <h3 data-lake-id="djw9K" id="djw9K"><span data-lake-id="u6246dc32" id="u6246dc32">CompletableFuture</span></h3>
  <p data-lake-id="ue0f9d8a7" id="ue0f9d8a7"><br></p>
  <p data-lake-id="u4b18ceae" id="u4b18ceae"><span data-lake-id="u1940396b" id="u1940396b">Java 8引入了CompletableFuture，它允许你异步执行任务并处理异常。你可以使用CompletableFuture.supplyAsync()来执行任务，并使用handle()方法捕获异常。</span></p>
  <p data-lake-id="u22cab2e8" id="u22cab2e8"><span data-lake-id="u23bccd5a" id="u23bccd5a">​</span><br></p>
  <p data-lake-id="u12bf49fb" id="u12bf49fb"><span data-lake-id="u06452e2e" id="u06452e2e">​</span><br></p>
  <pre lang="java"><code>
CompletableFuture&lt;Integer&gt; future = CompletableFuture.supplyAsync(() -&gt; {
    // 子线程抛出异常
    throw new RuntimeException("子线程异常");
});

future.handle((result, exception) -&gt; {
    if (exception != null) {
        System.out.println("捕获到子线程异常: " + exception.getMessage());
    } else {
        System.out.println("子线程结果: " + result);
    }
    return null;
});

</code></pre>
  <p data-lake-id="ucc288454" id="ucc288454"><br></p>
 </body>
</html>